package com.zlcx.tz.live.service.impl;

import com.google.common.collect.Lists;
import com.zlcx.tz.live.client.UserClient;
import com.zlcx.tz.live.constant.RedisKeyConst;
import com.zlcx.tz.live.domain.LiveFree;
import com.zlcx.tz.live.domain.LiveRoomInfo;
import com.zlcx.tz.live.domain.LiveRoomJoinRecord;
import com.zlcx.tz.live.domain.LiveSwitched;
import com.zlcx.tz.live.enums.RoomJoinStatus;
import com.zlcx.tz.live.enums.RoomMicStatus;
import com.zlcx.tz.live.enums.RoomState;
import com.zlcx.tz.live.enums.RoomUserRole;
import com.zlcx.tz.live.exception.BusinessException;
import com.zlcx.tz.live.repository.LiveFreeRepository;
import com.zlcx.tz.live.repository.LiveRoomJoinRecordRepository;
import com.zlcx.tz.live.repository.LiveSwitchedRepository;
import com.zlcx.tz.live.utils.DozerMapper;
import com.zlcx.tz.live.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.util.Asserts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.domain.Example;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
import static com.zlcx.tz.live.common.config.RedisConfig.CacheKeys.ROOM_FREE;
import static com.zlcx.tz.live.common.config.RedisConfig.CacheKeys.ROOM_SWITCHED;
import static com.zlcx.tz.live.common.config.RedisConfig.CacheKeys.ROOM_USER_LIST;

/**
 * @AUTHOR xhl
 * @CREATE 2019-04-18 17:42
 **/
@Service
@Slf4j
public class LiveRoomJoinRecordService {
    @Autowired
    LiveRoomJoinRecordRepository liveRoomJoinRecordRepository;
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    UserService userService;
    @Autowired
    LiveRoomPublisherService liveRoomPublisherService;
    @Autowired
    LiveRoomService liveRoomService;
    @Autowired
    LiveSwitchedRepository liveSwitchedRepository;
    @Autowired
    LiveFreeRepository liveFreeRepository;
    @Autowired
    UserClient userClient;

    @Value("${shangde.userDefaultPic}")
    private String userDefaultPic;
    private Logger traceLoger = LoggerFactory.getLogger("traceloger");


    @Cacheable(cacheNames = ROOM_SWITCHED, key = "#id")
    public LiveSwitched getSwitched(Long id){
        return liveSwitchedRepository.findOne(id);
    }

//    public VipUserVO checkVip(Long userId){
//        ResultVO<VipUserVO> resultVO = userClient.checkVip(userId);
//        return resultVO.getData();
//    }

    @Cacheable(cacheNames = ROOM_FREE, key = "#id")
    public LiveFree getLiveFree(Long id){
        return liveFreeRepository.findOne(id);
    }
    /**
     * 用户加入房间
     *
     * @param userId
     * @param roomId
     */
    @Caching(evict = {
            @CacheEvict(cacheNames = ROOM_USER_LIST, key = "#roomId")
    })
    public void userJoinRoom(Long userId, Long roomId, int userRole) {
        if (userId == null || roomId == null || RoomUserRole.getRoomUserRole(userRole) == null) {
            traceLoger.info("userJoinRoom【{}】加入房间【{}】，角色【{}】,参数不正确",userId,roomId,userRole);
            log.info("userJoinRoom param can not be null,userId={},roomId={},userRole={}", userId, roomId, userRole);
            return;
        }
        LiveRoomInfo liveRoomInfo = liveRoomService.getRoomInfoById(roomId);
        if (liveRoomInfo == null) {
            traceLoger.info("userJoinRoom【{}】加入房间【{}】，角色【{}】,房间已经关闭",userId,roomId,userRole);
            throw new BusinessException("房间已关闭");
        }
        List<LiveRoomJoinRecord> liveRoomJoinRecords = liveRoomJoinRecordRepository.findByUserIdAndJoinState(userId, RoomJoinStatus.IN.getCode());
        if (CollectionUtils.isNotEmpty(liveRoomJoinRecords)) {
            if (liveRoomJoinRecords.get(0).getRoomId().equals(roomId)) {
                return;
            }
            traceLoger.info("userJoinRoom【{}】加入房间【{}】，角色【{}】,正在其他房间连麦，加入记录id【{}】",userId,roomId,userRole,liveRoomJoinRecords.get(0).getId());
            throw new BusinessException("你正在连麦其他房间");
        }

        LiveRoomJoinRecord liveRoomJoinRecord = new LiveRoomJoinRecord();
        Date now = new Date();
        liveRoomJoinRecord.setUserId(userId);
        liveRoomJoinRecord.setRoomId(roomId);
        liveRoomJoinRecord.setJoinState(RoomJoinStatus.IN.getCode());
        liveRoomJoinRecord.setEnterTime(now);
        liveRoomJoinRecord.setUserMicState(liveRoomInfo.getRoomMicState());
        liveRoomJoinRecord.setUserSelfMicState(liveRoomInfo.getRoomMicState());
        liveRoomJoinRecord.setUserRole(userRole);
        liveRoomJoinRecord.setCreateTime(now);
        liveRoomJoinRecord.setRoleChangeTime(now);

        liveRoomJoinRecord = liveRoomJoinRecordRepository.save(liveRoomJoinRecord);

        traceLoger.info("userJoinRoom【{}】加入房间【{}】，角色【{}】,加入记录id【{}】",userId,roomId,userRole,liveRoomJoinRecord.getId());
        UserJoinVO userJoinVO = new UserJoinVO();
        DozerMapper.getInstance().map(liveRoomJoinRecord, userJoinVO);
        try {
            //TODO 去掉房间用户列表缓存
//            String hashKey = RedisKeyConst.getUserJoinRecordMapKey(roomId);
//            if(redisTemplate.hasKey(hashKey)){
//                redisTemplate.opsForHash().put(hashKey,"userId_"+userJoinVO.getUserId(),userJoinVO);
//            }
            if (userRole == 2) {
                String audienceCountKey = RedisKeyConst.getRoomAudienceCountKey(roomId.toString());
                redisTemplate.opsForValue().increment(audienceCountKey, 1);
            }
        } catch (Exception e) {
            log.error("userJoinRoom inbr cache error={}", e);
        }
    }


    /**
     * 获取房间观众数量
     *
     * @param roomId
     * @return
     */
    public int getAudienceCount(Long roomId) {
        String key = RedisKeyConst.getRoomAudienceCountKey(roomId.toString());
        try {
            Integer count = (Integer) redisTemplate.opsForValue().get(key);
            if (count != null && count > 0) {
                return count;
            }
        } catch (Exception e) {
            log.error("getAudienceCount get redis cache error={}", e);
        }
        int count = liveRoomJoinRecordRepository.countLiveRoomJoinRecordByRoomIdAndJoinStateAndUserRoleIn(
                roomId, RoomJoinStatus.IN.getCode(), Lists.newArrayList(RoomUserRole.AUDIENCE.getCode(), RoomUserRole.VISITOR.getCode()));

        if (count > 0) {
            try {
                redisTemplate.opsForValue().set(key, count);
            } catch (Exception e) {
                log.error("getAudienceCount set redis cache error={}", e);
            }
        }
        return count;
    }

    /**
     * 用户离开房间
     *
     * @param roomId
     * @param userId
     * @param exitType
     */
    @Caching(evict = {
            @CacheEvict(cacheNames = ROOM_USER_LIST, key = "#roomId")
    })
    public boolean exitRoom(Long roomId, Long userId, int exitType, int closeState) {
        traceLoger.info("exitRoom【{}】退出房间【{}】，角色【{}】,退出类型【{}】",userId,roomId,RoomJoinStatus.getRoomJoinStatus(exitType).getTips());
        UserJoinVO userJoinVO = getUserJoinByRoomIdAndUserIdNoStatus(roomId, userId);
        if (userJoinVO == null) {
            traceLoger.info("exitRoom【{}】退出房间【{}】，角色【{}】,退出类型【{}】,无加入房间记录",userId,roomId,RoomJoinStatus.getRoomJoinStatus(exitType).getTips());
            return false;
        }
        if (userJoinVO.getUserRole() == 2 || userJoinVO.getUserRole() == 3) {
            String audienceCountKey = RedisKeyConst.getRoomAudienceCountKey(roomId.toString());
            Long count = redisTemplate.opsForValue().increment(audienceCountKey, -1L);
            if (count < 0) {
                redisTemplate.opsForValue().set(audienceCountKey, 0L);
            }
        }
        liveRoomPublisherService.finishPublish(roomId,userId, closeState);
        liveRoomJoinRecordRepository.updateByRoomIdAndUserIdAndState(roomId, userId, exitType, new Date());

        // TODO 删除房间用户列表缓存
        //flushUserCache(roomId,userId);
        if(RoomJoinStatus.IN.getCode() != userJoinVO.getJoinState()){
            return false;
        }
        return true;
    }

    /**
     * 用户角色变更
     *
     * @param roomId
     * @param userId
     * @param userRole
     */
    @Caching(evict = {
            @CacheEvict(cacheNames = ROOM_USER_LIST, key = "#roomId")
    })
    public void roleChange(Long roomId, Long userId, int userRole) {
        traceLoger.info("roleChange 房间【{}】用户【{}】预切换角色【{}】",roomId,userId,RoomUserRole.getRoomUserRole(userRole).getTips());
        UserJoinVO userJoinVO = getUserJoinByRoomIdAndUserId(roomId, userId);
        if (userJoinVO == null) {
            traceLoger.info("roleChange 房间【{}】用户【{}】切换角色【{}】,无加入房间记录，切换失败",roomId,userId,RoomUserRole.getRoomUserRole(userRole).getTips());
            return;
        }
        if (userRole == RoomUserRole.OWNER.getCode()) {
            liveRoomJoinRecordRepository.roleChangeRoomOwner(roomId, userId, userRole, new Date());
        } else {
            liveRoomJoinRecordRepository.roleChange(roomId, userId, userRole, new Date());
        }
        traceLoger.info("roleChange 房间【{}】用户【{}】完成切换角色【{}】",roomId,userId,RoomUserRole.getRoomUserRole(userRole).getTips());
        // TODO 删除房间用户列表缓存
        // flushUserCache(roomId,userId);
        String audienceCountKey = RedisKeyConst.getRoomAudienceCountKey(roomId.toString());
        if (userRole == 2) {
            //上麦者变成观众，观众数量+1，上麦数量在别得地方控制
            redisTemplate.opsForValue().increment(audienceCountKey, 1L);
        }
        if (userRole == 0 || userRole == 1) {
            //观众变成上麦者,观众数量-1，上麦数量在别得地方控制
            Long count = redisTemplate.opsForValue().increment(audienceCountKey, -1L);
            if (count < 0) {
                redisTemplate.opsForValue().set(audienceCountKey, 0L);
            }
        }
    }

    /**
     * 用户麦状态变更
     *
     * @param roomId
     * @param userId
     * @param micState
     */
    @Caching(evict = {
            @CacheEvict(cacheNames = ROOM_USER_LIST, key = "#roomId")
    })
    public void micStateChange(Long roomId, Long userId, int micState) {
        traceLoger.info("micStateChange 房间【{}】用户【{}】切换麦克状态【{}】",roomId,userId, RoomMicStatus.getRoomMicStatus(micState).getTips());
        liveRoomJoinRecordRepository.micStateChange(roomId, userId, micState, new Date());
        redisTemplate.delete(RedisKeyConst.getRoomInfoByIdKey(roomId.toString()));
        // TODO 删除房间用户列表缓存
        // flushUserCache(roomId,userId);
    }

    /**
     * 刷新房间中用户缓存
     * @param roomId
     * @param userId
     */
    //TODO 去掉房间用户列表缓存
//    public void flushUserCache(Long roomId,Long userId){
//        String roomMapKey = RedisKeyConst.getUserJoinRecordMapKey(roomId);
//        try{
//            if(redisTemplate.opsForHash().hasKey(roomMapKey,"userId_"+userId)){
//                LiveRoomJoinRecord liveRoomJoinRecord = liveRoomJoinRecordRepository.findFirstByUserIdAndRoomIdAndJoinState(userId,roomId,RoomJoinStatus.IN.getCode());
//                redisTemplate.opsForHash().delete(roomMapKey,"userId_"+userId);
//                if(liveRoomJoinRecord!=null){
//                    UserJoinVO userJoinVO = new UserJoinVO();
//                    DozerMapper.getInstance().map(liveRoomJoinRecord,userJoinVO);
//                    redisTemplate.opsForHash().put(roomMapKey,"userId_"+userId,userJoinVO);
//                }
//            }
//        }catch (Exception e){
//            log.error("flushUserCache failed error={}",e);
//        }
//    }

    /**
     * 根据房间id获取房间用户信息
     *
     * @param roomId roomId
     * @return list
     */
    @Cacheable(cacheNames = ROOM_USER_LIST, key = "#roomId")
    public List<UserJoinVO> findUsersByRoomId(Long roomId) {
        //TODO 删除房间用户列表缓存
//        String mapKey = RedisKeyConst.getUserJoinRecordMapKey(roomId);
//        try{
//            List<UserJoinVO> userJoinVOS = (List<UserJoinVO>)redisTemplate.opsForHash().values(mapKey);
//            if(CollectionUtils.isNotEmpty(userJoinVOS)){
//                //找到对应的列表
//                List<UserJoinVO> resultVOS= userJoinVOS.stream().
//                        filter(vo->roleType.contains(vo.getUserRole())).collect(Collectors.toList());
//                //装载列表
//                resultVOS.stream().forEach(vo->{
//                    UserVO userVO = getFillUserInfo(vo.getUserId(),RoomUserRole.VISITOR.getCode()==vo.getUserRole());
//                    vo.setUserName(userVO.getName());
//                    vo.setUserAvatar(userVO.getAvatarUrl());
//                });
//
//                return resultVOS.stream().sorted(Comparator.comparing(UserJoinVO::getRoleChangeTime)).collect(Collectors.toList());
//            }
//        }catch (Exception e){
//            log.error("findUserListByRoomId get redis cache error={}",e);
//        }

        List<LiveRoomJoinRecord> liveRoomJoinRecords = liveRoomJoinRecordRepository.findByRoomIdAndJoinState(
                roomId, RoomJoinStatus.IN.getCode());

        if (CollectionUtils.isEmpty(liveRoomJoinRecords)) {
            return new ArrayList<>();
        }
        List<UserJoinVO> userJoinVOS = new ArrayList<>();
        DozerMapper.copyPropertiesList(liveRoomJoinRecords, userJoinVOS, UserJoinVO.class);
//        try {
//            Map<String, UserJoinVO> map = new HashMap();
//            for (int i = 0; i < userJoinVOS.size(); i++) {
//                map.put("userId_" + userJoinVOS.get(i).getUserId(), userJoinVOS.get(i));
//            }
//            //TODO 删除房间用户列表缓存
////            redisTemplate.opsForHash().putAll(mapKey,map);
//        } catch (Exception e) {
//            log.error("findUserListByRoomId set redis cache error={}", e);
//        }
        //找到对应的列表

        //装载数据
        userJoinVOS.stream().forEach(joinVO -> {
            UserVO userVO = getFillUserInfo(joinVO.getUserId(), RoomUserRole.VISITOR.getCode() == joinVO.getUserRole());
            joinVO.setUserName(userVO.getName());
            joinVO.setUserAvatar(userVO.getAvatarUrl());
            joinVO.setType(userVO.getType());
//            VipUserVO vipUserVO = userClient.getVipInfo(userVO.getId()).getData();
//            if(Objects.nonNull(vipUserVO)){
//                joinVO.setVipStatus(YesNoStatus.YES.getCode());
//            }else{
//                joinVO.setVipStatus(YesNoStatus.NO.getCode());
//            }

        });

        return userJoinVOS.stream().sorted(Comparator.comparing(UserJoinVO::getRoleChangeTime)).collect(Collectors.toList());
    }


    public UserVO getFillUserInfo(Long userId, boolean isVisitor) {
        UserVO userVO = null;
        if (!isVisitor) {
            userVO = userService.getUserInfo(userId);
        }
        if (userVO == null) {
            userVO = new UserVO();
        }
        if (StringUtils.isBlank(userVO.getUsername())) {
            userVO.setUsername("");
        }
        if (StringUtils.isBlank(userVO.getAvatarUrl())) {
            userVO.setAvatarUrl(userDefaultPic);
        }
        userVO.setId(userId);
        return userVO;
    }

    /**
     * 关闭房间，修改所有用户加入记录的状态
     *
     * @param roomId
     */
    public void closeRoomUpdateJoinRecordState(Long roomId, Integer joinState) {

        traceLoger.info("closeRoomUpdateJoinRecordState 关闭房间房间【{}】状态【{}】",roomId, RoomState.getRoomState(joinState).getTips());
        List<LiveRoomJoinRecord> list = liveRoomJoinRecordRepository.findByRoomIdAndJoinState(roomId,RoomJoinStatus.IN.getCode());
        if(CollectionUtils.isEmpty(list)){
            return;
        }

        liveRoomJoinRecordRepository.exitAll(joinState, new Date(), roomId);

        List<String> keys = new ArrayList<>();
//        keys.add(RedisKeyConst.getUserJoinRecordMapKey(roomId));
        keys.add(RedisKeyConst.getRoomAudienceCountKey(roomId.toString()));
        try {
            redisTemplate.delete(keys);
        } catch (Exception e) {
            log.error("closeRoomUpdateJoinRecordState delete cache error ={}", e);
        }
    }

    /**
     * 校验房主权限
     *
     * @param roomId
     * @param userId
     * @return
     */
    public boolean verifyOwner(Long roomId, Long userId) {
        LiveRoomJoinRecord liveRoomJoinRecord = liveRoomJoinRecordRepository.findFirstByUserIdAndRoomIdAndJoinStateAndUserRole(
                userId, roomId, RoomJoinStatus.IN.getCode(), RoomUserRole.OWNER.getCode());
        return liveRoomJoinRecord != null;
    }

    /**
     * 根据用户id房间id查询，用户加入记录信息
     *
     * @param roomId
     * @param userId
     * @return
     */
    public UserJoinVO getUserJoinByRoomIdAndUserId(Long roomId, Long userId) {
//TODO 删除房间用户列表缓存
        //        String key = RedisKeyConst.getUserJoinRecordMapKey(roomId);
//        try{
//            UserJoinVO userJoinVO = (UserJoinVO) redisTemplate.opsForHash().get(key,"userId_"+userId);
//            if(userJoinVO!=null){
//                return userJoinVO;
//            }
//        }catch (Exception e){
//            log.error("getUserJoinByRoomIdAndUserId redis cache fail error={}",e);
//        }

        LiveRoomJoinRecord liveRoomJoinRecord = liveRoomJoinRecordRepository.findFirstByUserIdAndRoomIdAndJoinState(userId, roomId, RoomJoinStatus.IN.getCode());

        if (liveRoomJoinRecord != null) {
            UserJoinVO userJoinVO = new UserJoinVO();
            DozerMapper.getInstance().map(liveRoomJoinRecord, userJoinVO);
            return userJoinVO;
        }
        return null;
    }

    public boolean isUserJoin(Long roomId, Long userId) {
        LiveRoomJoinRecord liveRoomJoinRecord = new LiveRoomJoinRecord();
        liveRoomJoinRecord.setRoomId(roomId);
        liveRoomJoinRecord.setUserId(userId);
        liveRoomJoinRecord.setJoinState(RoomJoinStatus.IN.getCode());
        return  liveRoomJoinRecordRepository.count(Example.of(liveRoomJoinRecord))>0;
    }

    public UserJoinVO getUserJoinByRoomIdAndUserIdNoStatus(Long roomId, Long userId) {

        UserJoinVO userJoinVO = getUserJoinByRoomIdAndUserId(roomId,userId);
        if(!Objects.isNull(userJoinVO)){
            return userJoinVO;
        }
        LiveRoomJoinRecord liveRoomJoinRecord = liveRoomJoinRecordRepository.findFirstByUserIdAndRoomIdOrderByRoleChangeTimeDesc(userId, roomId);
        if (liveRoomJoinRecord != null) {
            userJoinVO = new UserJoinVO();
            DozerMapper.getInstance().map(liveRoomJoinRecord, userJoinVO);
            return userJoinVO;
        }
        return null;
    }

    /**
     * 异常退出
     *
     * @return
     */
    public List<LiveRoomJoinRecord> getNotNonalQuitUser() {
        return liveRoomJoinRecordRepository.getNotNonalQuitUser();
    }

    /**
     * 查询房主
     *
     * @param roomId
     * @return
     * @throws Exception
     */
    public LiveRoomJoinRecord getHostUserJoinRecord(Long roomId) {
        return liveRoomJoinRecordRepository.findFirstByRoomIdAndUserRoleOrderByJoinStateAscRoleChangeTimeDesc(roomId, RoomUserRole.OWNER.getCode());
    }

    /**
     * 查询当前用户加入的房间
     *
     * @param userId
     * @return
     */
    public LiveRoomJoinRecord getUserJoinRecord(Long userId) {
        return liveRoomJoinRecordRepository.findFirstByUserIdAndJoinStateOrderByRoleChangeTimeDesc(userId, RoomJoinStatus.IN.getCode());
    }

    /**
     * 查询当前用户最新连麦学习的房间记录
     *
     * @param userId
     * @return
     */
    public LiveRoomJoinRecord getLastUserJoinRecord(Long userId) {
        return liveRoomJoinRecordRepository.findFirstByUserIdOrderByRoleChangeTimeDesc(userId);
    }

    /**
     * 查询用户连麦房间状态
     *
     * @param userRole  房间用户角色
     * @param joinState 连麦中
     * @param ids       用户id集合
     * @return
     */
    public List<UserLiveStateVO> findByUserRoleAndJoinStateAndUserIdIn(Integer userRole, Integer joinState, List<Long> ids) {
        Asserts.check(!ids.isEmpty(), "用户id list不能为空!");
        return liveRoomJoinRecordRepository.findByUserRoleAndJoinStateAndUserIdIn(userRole, joinState, ids);
    }

}
